﻿using DllCallAnalysis45;
using DrawIO45.Models.DrawIOs;
using DrawIO45.Models.Logic;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace DrawIO45.Draws.Business
{
    /// <summary>
    /// 花出来依赖类
    /// </summary>
    public class DrawDependentClass : IDrawOnRoot
    {
        DrawDependentClassOption Option;

        public DrawDependentClass()
        {
            Option = new DrawDependentClassOption();
        }

        public DrawDependentClass(DrawDependentClassOption option)
        {
            Option = option;
        }

        public void DrawOnRoot(Root root, List<DILocationInfo<LClassInfo>> classInfos)
        {
            var types = classInfos.Select(n => n.Object.Source).ToArray();

            if (Option.IgnoreEmptyMethodClass)
            {
                var _types = new List<Type>();

                foreach (var item in types)
                {
                    if (IsEmptyMethodClass(item))
                        continue;

                    _types.Add(item);
                }

                types = _types.ToArray();
            }

            // 画类引用的线
            foreach (var itemClassInfo in classInfos)
            {
                var currentType = itemClassInfo.Object.Source;
                if (DrawHelper.CanFindCellByLClass(root, itemClassInfo.Object) != true)
                    continue;

                var methods = itemClassInfo.Object.MethodInfos;
                if (methods?.Any() != true)
                    continue;

                var dependentTypes = GetDependentType(types.ToArray(), itemClassInfo.Object.Source)
                    ?.Where(n => FilterOption(currentType, n))
                    .ToList();

                foreach (var itemDependentType in dependentTypes)
                {
                    var targetLInfo = classInfos.FirstOrDefault(n => n.Object.Source == itemDependentType);
                    if (targetLInfo == null)
                        continue;

                    if (DrawHelper.CanFindCellByLClass(root, classInfos, itemDependentType) != true)
                        continue;

                    root.MxCells.Add(new MxCell
                    {
                        Id = Guid.NewGuid().ToString(),
                        Parent = "1",
                        Style = "endArrow=open;endSize=12;dashed=1;html=1;rounded=0;exitPerimeter=0;",
                        Edge = "1",
                        Source = itemClassInfo.Object.Id,
                        Target = targetLInfo.Object.Id,
                        Value = "Use",
                        MxGeometries = new List<MxGeometry>
                        {
                            new MxGeometry
                            {
                                Width = "160",
                                Relative = "1",
                                As = "geometry",
                            },
                        },
                    });
                }
            }
        }

        string[] PropertyMethodHeads = new[] { "get_", "set_" };

        bool IsEmptyMethodClass(Type type)
        {
            var methods = type.GetMethods()
                //?.Where(n => n.DeclaringType != null && n.DeclaringType != typeof(object))
                ?.Where(n => n.DeclaringType == type)
                .Where(n => PropertyMethodHeads.Any(p => n.Name.StartsWith(p)) != true)
                .ToList();

            return methods?.Any() != true;
        }

        bool FilterOption(Type source, Type type)
        {
            if (source == type)
                return false;

            // 存在继承关系时，不输出引用关系
            if (Option.IgnoreWhereExtend)
            {
                if (type.IsAssignableFrom(source))
                    return false;
            }

            if (type.IsInterface)
            {
                if (Option.AllowUseTypes?.Contains(AllowUseType.Inteface) != true)
                    return false;
            }

            if (type.IsClass)
            {
                if (Option.AllowUseTypes?.Contains(AllowUseType.Class) != true)
                    return false;
            }

            if (Option.AllowTypeFilter != null)
            {
                if (Option.AllowTypeFilter(source, type) != true)
                    return false;
            }

            return true;
        }

        List<Type> GetDependentType(Type[] allTypes, Type type)
        {
            if (type == null)
                return null;

            var list = new List<Type>();

            if (type.IsClass)
            {
                // 如果是泛型类，要把参数也添加到引用上
                if (type.IsGenericType)
                {
                    AddGenericDependent(type);
                }

                if (type.BaseType?.IsGenericType == true)
                {
                    AddGenericDependent(type.BaseType);
                }
            }

            var methods = type.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);

            if (methods?.Any() == true)
            {
                //var dependentTypes = methods.SelectMany(n => MethodAnalysisHelper.GetMethodCallMethods(allTypes, n.Source) ?? new List<MethodInfo>())
                //    .Select(n => n.DeclaringType)
                //    .Distinct()
                //    .Where(n => FilterOption(currentType, n))
                //    .ToList();

                foreach (var itemMethod in methods)
                {
                    var paramters = itemMethod.GetParameters();
                    if (paramters?.Any() == true)
                        list.AddRange(paramters.Select(n => n.ParameterType));

                    if (itemMethod.ReturnType != typeof(void))
                        list.Add(itemMethod.ReturnType);

                    var callMethods = MethodAnalysisHelper.GetMethodCallMethods(allTypes, itemMethod);
                    if (callMethods?.Any() == true)
                    {
                        foreach (var itemCallMethod in callMethods)
                        {
                            // 添加被调用函数的所在类
                            list.Add(itemCallMethod.DeclaringType);

                            var callMethodParamters = itemCallMethod.GetParameters();
                            if (callMethodParamters?.Any() == true)
                                list.AddRange(callMethodParamters.Select(n => n.ParameterType));

                            if (itemCallMethod.ReturnType != typeof(void))
                                list.Add(itemCallMethod.ReturnType);
                        }
                    }
                }
            }

            list = list.Distinct().Intersect(allTypes).ToList();

            return list;

            void AddGenericDependent(Type _c)
            {
                var paramters = _c.GetGenericArguments();
                if (paramters?.Any() == true)
                    list.AddRange(paramters);
            }
        }
    }

    public class DrawDependentClassOption
    {
        /// <summary>
        /// 继承时忽略引用关系
        /// </summary>
        public bool IgnoreWhereExtend { get; set; }

        /// <summary>
        /// 忽略无函数的类
        /// </summary>
        public bool IgnoreEmptyMethodClass { get; set; }

        /// <summary>
        /// 允许使用的类型
        /// </summary>
        public AllowUseType[] AllowUseTypes { get; set; } = new AllowUseType[]
        {
            AllowUseType.Class,
            AllowUseType.Inteface,
        };

        /// <summary>
        /// 允许过滤类型
        /// type:Source; Type: type
        /// </summary>
        public Func<Type, Type, bool> AllowTypeFilter { get; set; }
    }

    public enum AllowUseType
    {
        Inteface,
        Class,
    }
}
